package org.moyakarta.rest;

import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.logging.Logger;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.FetchOptions;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Query.FilterOperator;
import com.google.appengine.api.datastore.Query.SortDirection;

public class PlaceStorageInGoogleDataStore
{

   private static final DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();

   private static final int ROWS_NUM_IN_GET_ALL = 10;

   private static final int ROWS_NUM_IN_DELTE_ALL = 1000;

   private static final Logger log = Logger.getLogger(PlaceStorageInGoogleDataStore.class.getName());

   public PlaceStorageInGoogleDataStore()
   {
      // default
   }

   public Place getPlace(String hash)
   {
      log.info("get one = hash = " + hash);
      Query q = new Query("Place");
      q.addFilter("hash", FilterOperator.EQUAL, hash);
      Entity entity = datastore.prepare(q).asSingleEntity();
      Place result = entityToPlace(entity);
      return result;
   }

   public String putPlace(Place place)
   {
      String hash = place.getHash();
      if (hash == null || hash.trim().length() == 0)
      {
         hash = generateId();
         place.setHash(hash);
      }
      log.info("put one = hash = " + hash);

      Entity placeEntity = new Entity("Place");

      placeEntity.setProperty("hash", place.getHash());
      placeEntity.setProperty("name", place.getName());
      placeEntity.setProperty("mlng", place.getMlng());
      placeEntity.setProperty("mlat", place.getMlat());
      placeEntity.setProperty("lng", place.getLng());
      placeEntity.setProperty("lat", place.getLat());
      placeEntity.setProperty("zoom", place.getZoom());
      placeEntity.setProperty("mapProvider", place.getMapProvider());
      placeEntity.setProperty("mapType", place.getMapType());
      placeEntity.setProperty("mapW", place.getMapW());
      placeEntity.setProperty("mapH", place.getMapH());
      placeEntity.setProperty("createdBy", place.getCreatedBy());
      //      placeEntity.setProperty("creationDate", place.getCreationDate());

      datastore.put(placeEntity);

      return hash;
   }

   public Collection<Place> getAll(int rowsNum, int offset)
   {
      log.info("get all = ");
      if (rowsNum <= 0)
         rowsNum = ROWS_NUM_IN_GET_ALL;
      if (offset < 0)
         offset = 0;
      Collection<Place> places = new HashSet<Place>();
      Query q = new Query("Place");
      q.addSort("hash", SortDirection.ASCENDING);
      FetchOptions fetchOptions = FetchOptions.Builder.withLimit(rowsNum).offset(offset);
      List<Entity> entities = datastore.prepare(q).asList(fetchOptions);
      for (Entity entity : entities)
      {
         Place place = entityToPlace(entity);
         places.add(place);
      }
      return places;
   }

   public void deletePlace(String hash)
   {
      log.info("delete one = hash = " + hash);
      Query q = new Query("Place");
      q.addFilter("hash", FilterOperator.EQUAL, hash);
      Entity entity = datastore.prepare(q).asSingleEntity();

      log.info("delete with key = " + entity.getKey());
      datastore.delete(entity.getKey());
   }

   public void deleteAll()
   {
      log.info("delete all = ");
      Query q = new Query("Place");
      FetchOptions fetchOptions = FetchOptions.Builder.withLimit(ROWS_NUM_IN_DELTE_ALL);
      List<Entity> entities = datastore.prepare(q).asList(fetchOptions);
      for (Entity entity : entities)
      {
         datastore.delete(entity.getKey());
      }
   }
   
   public int countAll()
   {
      log.info("count all = ");
      int count = 0;
      
      Query q = new Query("Place");
      q.addSort("hash", SortDirection.ASCENDING);
      
      FetchOptions fetchOptions = FetchOptions.Builder.withOffset(count);
      int size = datastore.prepare(q).countEntities(fetchOptions);
      while (size > 0)
      {
         count += size;

         fetchOptions = FetchOptions.Builder.withOffset(count);
         size = datastore.prepare(q).countEntities(fetchOptions);
      }
      return count;
   }

   public int queryCount(double fromLat, double toLat, double fromLng, double toLng, String sort)
   {
      log.info("QUERY count = ");
      int count = 0;
      
      Query q = new Query("Place");
      if (sort != null && !"".equals(sort))
        q.addSort(sort, SortDirection.ASCENDING);
      if (fromLat > 0)
         q.addFilter("lat", FilterOperator.GREATER_THAN, fromLat);
      if (toLat > 0)
         q.addFilter("lat", FilterOperator.LESS_THAN, toLat);
      if (fromLng > 0)
         q.addFilter("lng", FilterOperator.GREATER_THAN, fromLng);
      if (toLng > 0)
         q.addFilter("lng", FilterOperator.LESS_THAN, toLng);
      
      FetchOptions fetchOptions = FetchOptions.Builder.withOffset(count);
      int size = datastore.prepare(q).countEntities(fetchOptions);
      while (size > 0)
      {
         count += size;

         fetchOptions = FetchOptions.Builder.withOffset(count);
         size = datastore.prepare(q).countEntities(fetchOptions);
      }
      return count;
   }
   
   private synchronized String generateId()
   {
      String id = PlaceGenerateUtils.generateEncodedHash();
      while (hasPlace(id))
      {
         id = PlaceGenerateUtils.generateEncodedHash();
      }
      return id;
   }

   private Place entityToPlace(Entity entity)
   {
      Place place = new Place();
      place.setHash((String)entity.getProperty("hash"));
      place.setName((String)entity.getProperty("name"));
      place.setMlng((Double)entity.getProperty("mlng"));
      place.setMlat((Double)entity.getProperty("mlat"));
      place.setLng((Double)entity.getProperty("lng"));
      place.setLat((Double)entity.getProperty("lat"));
      place.setZoom(((Long)entity.getProperty("zoom")).intValue());
      place.setMapProvider((String)entity.getProperty("mapProvider"));
      place.setMapType((String)entity.getProperty("mapType"));
      place.setMapW(((Long)entity.getProperty("mapW")).intValue());
      place.setMapH(((Long)entity.getProperty("mapH")).intValue());
      place.setCreatedBy((String)entity.getProperty("createdBy"));
      //      place.setCreationDate((Date)entity.getProperty("creationDate"));
      return place;
   }

   private boolean hasPlace(String hash)
   {
      log.info("get one = hash = " + hash);
      Query q = new Query("Place");
      q.addFilter("hash", FilterOperator.EQUAL, hash);
      Entity entity = datastore.prepare(q).asSingleEntity();
      return entity != null;
   }
   
}
